///////////////////////////////////////////////////////////////
//  Copyright Christopher Kormanyos 2002 - 2011.
//  Copyright 2011 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
//
// This work is based on an earlier work:
// "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations",
// in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469

#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#endif

#include <boost/detail/lightweight_test.hpp>
#include <boost/array.hpp>
#include "test.hpp"

#if !defined(TEST_MPF_50) && !defined(TEST_MPF) && !defined(TEST_BACKEND) && !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR) && !defined(TEST_MPFR_50) && !defined(TEST_MPFI_50) && !defined(TEST_FLOAT128) && !defined(TEST_CPP_BIN_FLOAT)
#define TEST_MPF_50
//#  define TEST_MPF
#define TEST_BACKEND
#define TEST_CPP_DEC_FLOAT
#define TEST_MPFI_50
#define TEST_FLOAT128
#define TEST_CPP_BIN_FLOAT

#ifdef _MSC_VER
#pragma message("CAUTION!!: No backend type specified so testing everything.... this will take some time!!")
#endif
#ifdef __GNUC__
#pragma warning "CAUTION!!: No backend type specified so testing everything.... this will take some time!!"
#endif

#endif

#if defined(TEST_MPF_50)
#include <boost/multiprecision/gmp.hpp>
#endif
#if defined(TEST_MPFR_50)
#include <boost/multiprecision/mpfr.hpp>
#endif
#if defined(TEST_MPFI_50)
#include <boost/multiprecision/mpfi.hpp>
#endif
#ifdef TEST_BACKEND
#include <boost/multiprecision/concepts/mp_number_archetypes.hpp>
#endif
#ifdef TEST_CPP_DEC_FLOAT
#include <boost/multiprecision/cpp_dec_float.hpp>
#endif
#ifdef TEST_FLOAT128
#include <boost/multiprecision/float128.hpp>
#endif
#ifdef TEST_CPP_BIN_FLOAT
#include <boost/multiprecision/cpp_bin_float.hpp>
#endif

template <class T>
struct has_poor_large_value_support
{
   static const bool value = false;
};
#ifdef TEST_CPP_DEC_FLOAT
template <unsigned Digits10, class ExponentType, class Allocator, boost::multiprecision::expression_template_option ExpressionTemplates>
struct has_poor_large_value_support<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>, ExpressionTemplates> >
{
   static const bool value = true;
};
#endif
#ifdef TEST_CPP_BIN_FLOAT
template <unsigned Digits, boost::multiprecision::backends::digit_base_type DigitBase, class Allocator, class Exponent, Exponent MinE, Exponent MaxE, boost::multiprecision::expression_template_option ExpressionTemplates>
struct has_poor_large_value_support<boost::multiprecision::number<boost::multiprecision::cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE, MaxE>, ExpressionTemplates> >
{
   static const bool value = true;
};
#endif

template <class T>
void test()
{
   std::cout << "Testing type " << typeid(T).name() << std::endl;
   static const boost::array<const char*, 101u> data =
       {{
           "-2.37609908807915949996042688873953402912174184373388399043229539427530802169622688886435380890546981798452174137747437590e-1",
           "8.03406366226813589517543567844755380935198206635917017883860879215939165740799963435747185200486086864198723786516760875e-1",
           "8.60219386510802105228997694366289682807721120146423711696179175800635220710279361583231346318224971127450760223168489952e-1",
           "-1.36768951513839774357595871594675554406872039078811749027554673949684004409484639336417431285061889554892096426752261915e-1",
           "-9.66210139195431691033548069227792927999642647449593184440815029076272297050360196975341458076547426373476590671462150981e-1",
           "-6.12007278553856790723803948280976098970972124581361775428331444376106018942231526074915731012122426588769327127413045994e-1",
           "4.91927698740873688392439262912409276430264703350691359723802294639643655296838880236042651349290074585311025856549893171e-1",
           "9.93232596718899824059271235487971663771012607519717340071654721877802691370866768064059943491135925674950430467047724563e-1",
           "2.77789911520199551017947550534057049374212876971194676010301098598339529915403722848373365985645657342475739669568931563e-1",
           "-7.77955945956221239101360662190442739163791527953499629555756394261998892874934847131138921705713935365505245406994428077e-1",
           "-8.80676278306736581575818642341143682410874043182925227659938804267878718513212454821032629378618345485453587099696563832e-1",
           "9.54652155963865007116798560589970996367213754762169439269792747771200843006278637115722685610960738675814993576019945344e-2",
           "9.54658201427917718824191302196929158303422390793460018465335986921801519149657723689322277773550748806000948225466432438e-1",
           "6.44358700620889799575033272322899136331490664925359198096632560532437137894857803619177106562399406351419810452110265174e-1",
           "-4.55304635273050571206400777159475409897339683148730716647371922365967582339285347105376503917296765204188604297021364549e-1",
           "-9.97202532932553753622481171186283382950122646390227670693679248197349800205205290898290539070732778341271049474946533154e-1",
           "-3.17489525058325500707686194437148362752290391406825231198381521862930317513649081353670386166519524315810546189268634469e-1",
           "7.51160186640147504067744846462384089742696250681200524524912647858645140367792164416711871535116761744380912486921554617e-1",
           "8.99610194168373157174515848193119670768490559799348397680196213249921436405001710937402190319584272526657508442591319630e-1",
           "-5.39963892484342940823660554048696208293700871414984387094529796385334086703752106515008832585578915389731907422242902573e-2",
           "-9.41455348900839346761557896365239742769987576963330061702397697388879776230596944312519157729410022380228287314835345969e-1",
           "-6.75595816763857390859268297670835380459024839344154743310231115864242050771191159334664874097564054770066166961642073448e-1",
           "4.17894201894880415042381733708896725748531223743344790054523182948440843948904650988733732381978194392219295696279423635e-1",
           "9.99447981389824371458566861195586395552622718284098766856978062347139060489410032781030191080200904443096549587568037683e-1",
           "3.56640095868759075150409032448421838265699043643228482503057299699740924345262819242042067863780263400092250418388628640e-1",
           "-7.23065426868134142613141384486526262450487633432466529798821958977732347646832059032382447792655111641456570392189752211e-1",
           "-9.16988391192434436877664999042786024703848714036221388727578305299843547352325574309860356272561772723624753484063972217e-1",
           "1.24341855683226931265962750806821531283439413068694552738675989282017066737438591268502070364982899342633928417210588531e-2",
           "9.26624413643579136646620112107410908114766812511471130116341925013001661546817531064974089666536893346764523464250445838e-1",
           "7.05664607841658050248613256866289182754229289446384595719719495272020558391145537620968819401219414243210529013148958366e-1",
           "-3.79761093422301890838671114556341706055562482358183402807224298533060188038450560241345615647754569347252101282386222173e-1",
           "-9.99965058979463689113370264378210962384824970050865061898892508056811665771886385589295806419278045318841717598003331419e-1",
           "-3.95173919871548266286836251448043149039940610894391718791244019288314418437411707835924620250473697245151743147215758686e-1",
           "6.93720251624319621941983929806434090162802383400620564454074825718151625795576680427510026452063593762417421561201654156e-1",
           "9.32780816819880202610269817418700102084277332259524791943833699964920012022753227823298655560837271746316929623378078695e-1",
           "2.91495208658083070508005579692813621670878962971611104453227900103973434149303469066898102622556226584993895360896300290e-2",
           "-9.10191043170480685360743788297835112117551819731152897291272407935139876953384666161532187572493791297095784055525159185e-1",
           "-7.34513075127503122343910106816656237074878218180284276449954797048122379869002663646507706411949095015624141821039453971e-1",
           "3.40971254411713599427147477626159847871020791931107106418841144080445813896332252160005593096670674809345703079384115052e-1",
           "9.98752871506016936810666998588493462933191829230756181478046320353377054175122206889047094521687205093218701141334147081e-1",
           "4.33024359542714849537532946954507232835434973891665238942502273464321666207117525270650546741831354943253652514490075246e-1",
           "-6.63175408268187738636594884921931867786416057472876635147295424128144233911929585327601381618059327766986981109409782838e-1",
           "-9.46960160806563725719808910991708075372282242401645009270517113290439792088443109178772446465191984149998211903724560065e-1",
           "-7.06828182905581345108929510344440443421290640066613022421187316650733628538705972455891575947230299102119854983197703150e-2",
           "8.92183656127948379886438402777950080111433733329436790239129260607557296960582455582584117031260710927499215646838011844e-1",
           "7.62091330231640362984555508176991632755732840163230620595759320390970951235395195394649584713540498911356433919369698423e-1",
           "-3.01591765120371930643555588643712101466544136366607065361801475091335195383846047491935017919396438040414024941341524187e-1",
           "-9.95813515236177554177387795413035497724212540625760091518605741283184405719984044075159457509720410668598540884613985023e-1",
           "-4.70125959152223022135690700550251564040118601846181392455764893020377582359429013073566263451488554529709131439092909247e-1",
           "6.31483718775865440843182928017874708719203714677143270278178885379757350754752477512514449375355491054871712891789652146e-1",
           "1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
           "3.87481045630097871028201331640164477694000480353342897357083794605539506785023570062767753819472037935010414476627195076e-1",
           "-6.99716878554812023132822640034166338740077082416915046841339368979161296335086955803240899441098534898926193252558848693e-1",
           "-9.29735101124991407032113033015438177585645722877060262785796302722011806301680288369763295777635760805579255008366302180e-1",
           "-2.07925797310208845709174899248298159909010721767168338004516304026594885686024530923209628704670627250569637267490913462e-2",
           "9.13621640053945104047985280883096238900189007462190433514805702721127019097915088259906184736442916334750420359005079959e-1",
           "7.28814716527795995795780587639833705107995825167970668466246348938821997240383021826681253777014938636567626203524137506e-1",
           "-3.48817863192357573536083313907744269588018702862402502601699983054718835012048372083235768555953613790431700360695537918e-1",
           "-9.99135337256258278958854200595331742602280601557283993231562055717063052179292021052372510863057206152466386086657442382e-1",
           "-4.25474147219713305654097901385832164424798803616095278869643928816776198489330071073326518019520590535564717523756486665e-1",
           "6.69409002349720857726983952566596052122726437391092096671257338244059819036586172017092390651026654050631669646310910927e-1",
           "9.44240747589054266930136705015787520728797286842746645573763175559132491628984502333043316023599487896169049499868916865e-1",
           "6.23417820549832676844933182722733277887833220127638406914080428946880981800946578131712749910941183940944526141109771339e-2",
           "-8.95928229794837090592434136137683839101829890460754766346170956577979909285084363961363023377587044303560652568203578379e-1",
           "-7.56652196635835430995109388017590459596111729342964992503572530290981857981790620732129221157071082788019187787999930361e-1",
           "3.09551461133309219674309651201007026752336319933918953736299690287371505733386433918863300129026763968979930342098019300e-1",
           "9.96542844308353945757715814452637400116215448012622700698887257177706625278927018059066920597035660000571997275705962011e-1",
           "4.62731465522276407764000677957978862104808823938378826581864482071733508960062598574638347814740748458034065235894523010e-1",
           "-6.37943500095315456672541800050589670140910744260281868746767523730582697622604545933849801882909439609368258115114388202e-1",
           "-9.57113494461990955768932962010819183910065445494138937651443249061391692258872250121438832853873660881630205561168895590e-1",
           "-1.03783175146302830356973378710923690121182237844646430783773333188328606275124873219756415071202025673009660963930966273e-1",
           "8.76685468012988943166112725030293740012198666616661362437433807594683780986916439696374569274720383546275206733493672834e-1",
           "7.83181178815072182812568575402104911406191663934571600092749188502783771503475038116599065276589122015600004250624262132e-1",
           "-2.69749743842835294071354429049113807280228918034124159074991560056663623624511602063409428877143567459307323934051784210e-1",
           "-9.92227004420417932443416371636723983768124774541445787394585828303853075015733744933294181104002649816119116502663362907e-1",
           "-4.99188570507651271652464431008309802023236218596632956064119419694573621896525872847587264755853127438644874992889777436e-1",
           "6.05374785886620830935500306718810628353011877048386199574451402773468315797082901705593423724389976967865835641164117478e-1",
           "9.68331080574540181354402420018944004334504868848934676984951546671476956051983469715128604348963016773169794077158289730e-1",
           "1.45045093347669933436797325432376656017480150281100519869038554695618054318368975927557872948037203212941966706926758604e-1",
           "-8.55926631706799584065153976496431313099942493164544290051315786450857575707615522517293656706329757352795226750189755758e-1",
           "-8.08355785820466703104647317116964786017731181599256098405978700298563758509572188640629418770593008092680980412866065299e-1",
           "2.29481541445091823694157468006983283649885473964756916206813927875661041834620526229807744443043682778028709792615798955e-1",
           "9.86195281084368344446227722442335005500018635181693920385626820970119467136148305491035657795704047666385553672680209923e-1",
           "5.34782415974986828906369231191245075731384342252264783019973387059318216570499447505623911253042567598873910043381675873e-1",
           "-5.71759181631212640256161075896307515511612057247572886814941945052483422285718810088660759708176904381865453799197101481e-1",
           "-9.77874107069129472007009478090869879295520839405452365411822873037906082086100232576241983901051327761231156641104065497e-1",
           "-1.86056181372276495846711248156316208757691570931906856005697361080864028851991674077024223201008430626166447144444086146e-1",
           "8.33687619660983803179149188531271900120055171980951416163724579833511897001564116810390933587615557717585362295882429826e-1",
           "8.32132482562487183916482822112362004641509381783438374175226355792137053285527706239574867923387554339582561002247202518e-1",
           "-1.88816490768820368180947188938258919466912959009058295775702554895970560887437777994365295452696990115940651570073217522e-1",
           "-9.78458105113103660973458641126689066610890753079836635611789969774219913050456840122278188955139015473252491612410972950e-1",
           "-5.69451448580118869157805059117807250106203230622762838051154208713065707949727035884250775206017528612930773233017928006e-1",
           "5.37154819650306918873973169624898539483418364490295996462663218848771864764982600193558748568095521886456306061269765631e-1",
           "9.85726070946814004698231423834505649751779161578718404221294527194525251740198034173542003704080544827976936213857653517e-1",
           "2.26745517700332138489400566746499809209783385009289423848083137846668382711005704387134606000570923556980021574851618566e-1",
           "-8.10006890365888881023982873786181048364505748637138923322482323010218991062084191379116946709356002103893071903481540337e-1",
           "-8.54470151393449484710948210543666267680196067632693416660536443330720708402601669617638569732848938319544250428600991723e-1",
           "1.47824914922605209542648603104533928946885824995208478684499907657728115943168395067575842431291755277452367320596435067e-1",
           "9.69028856602232134498324179654622883463820270279077886397861028881882684131282848087869087883519707948141915733221980948e-1",
           "6.03135714281336943093251136556365407562473924416812270469171432809743173719168209727199952532489544254928975940518615351e-1",
           "-5.01621542149055350065079347615664213658089623368745676779267390227688581807037821041573344917735076902116221444127518632e-1",
       }};

   std::uintmax_t max_err = 0;
   for (unsigned k = 0; k < data.size(); k++)
   {
      static const T euler_gamma = static_cast<T>("5.77215664901532860606512090082402431042159335939923598805767234884867726777664670936947063291746749514631447249807082480960504014486542836224173997644923536253500333742937337737673942792595258247094916008735203948165670853233151776611528621199501507984793745085705740029921354786146694029604325421519e-1");
      T              val         = cos(euler_gamma * ((100 * k) - 5000));
      T              e           = relative_error(val, T(data[k]));
      unsigned       err         = e.template convert_to<unsigned>();
      if (err > max_err)
         max_err = err;
      val = cos(-euler_gamma * ((100 * k) - 5000));
      e   = relative_error(val, T(data[k]));
      err = e.template convert_to<unsigned>();
      if (err > max_err)
         max_err = err;
   }
   std::cout << "Max error was: " << max_err << std::endl;
   BOOST_TEST(max_err < 5000000000ULL);

   static const boost::array<const char*, 51u> near_one =
       {{
           "0.001103662615143147017601393734232421549156847219973314118949247990939859507971857976111288683988415478257878902917354105871236439563170045857810738102729287303674642020538722348587741395785016549139854168605590336633274890874911975139498047488076101124170425114828336211817830671418321600962996502656221412102902998671173649312277667706874059520046508702908872747388744526457997404065469953155187340837",
           "0.0030157041572432190019717764776322147049224887913331212054173669461673516770769148735558540290915754514269097799280330158745508542936966046640795712691698490758472574837678659161855207596563250546269614348515005196657786359646020537231301121492728577410212550350070502426482802490452572415879214994201864402901595309726458026672888041860289772080407882673424767232857435141510615538430445535588448032261",
           "0.0049277346741750694119109616231402901360500185022482787362616412856233338679208830974124749830793778323997684640684587346403570647040211088376986993615001420565608501262335822815041623056442245053684626079448221530207757672939048030945164948521320401574074802966260975904478775314730002799895049652983052125151436511589335123254582883803928373794668948878049089088843407672795237410750365571990834236348",
           "0.0068397471757112108044544339281399968563253858411528016363900245610068161564091499398024110635089866212789583403491711476788985949178019078796740862827299289838108117219486357027460681238178010067475221327133396780718266880540272936860185783976937569903778160170225166328920201699827472893824363838439627437187765159770262282930293057633886299749088561182830982345936603553661004526800797082995898813903",
           "0.008751734671690018552720976028788304981263251327799787402847843531098338033456389098956160956160597460869666211569183483587979772678557230743050583975270977754518475970489741156524119378847305446845331098526962579804361960647344836744631465315238254178161716870409320747176674142306881908614436607977326507761331050644035013756409444911127742401762647885817361924274132830920030936589562922803846509607",
           "0.010663690172041286306192948782895990881227033104763005629047563262531418373465237876680529005058533981010793759446966868628480908709812402179047774888094761104238131843239037167212206469894485599085717216855637620318723457013401360290343196462723174177342294339229792213396006286331141933952648238459364417067676760977202812721209681621184724443514062993874571816382775241753902032787099435678672474841",
           "0.012575606686811781116778815864446643746718166744927923785139233710240017201737625088406861185781739628293213436323314376708933534933748436720721568462267317026688561438144113855290140925120435317506169856461386623828941531577425811393721991195661429557079268566156800689149725770186985872449679872155877537432897653904213375816262709009448220490217917537801655330411526526290708967440087145630462068976",
           "0.014487477226190798137230457835664944872810802211308342685055637400305474021552028493778559102552025380880477103065187688337931713540557562373991431880251553620947836940726544553647739994157300967262941976933375115946629204615918888689039777423610894099269527633757403818072000860056651445780074486612843419789636984117740884711882675016016919064788076097669389652323393864660933705259163296837955978748",
           "0.016399294800535714798489384358524900105286836154421890562438917862501411947245037986455919696365301666112414890097246479762149456962809638588245466988584093380263545543023411087203351948681559905336010066900667101787138862856447778428938265451537422367650992162004154495705878092019403922006152498705687706013973674800621422751806347308220226407830977673540994341535381202679482907460136241543505771361",
           "0.018311052420397544372537858201001145083914223029622271478159852925692124442640247100336262041440360412392318902558295108452513123454103504516607498763863728284797202978975409264141138861738184468257576157204552383675442780049333490201800526935793000727771598103735596829143729912656158790924063286176336412427525710457367751437348230246446466833498557166002214984255422115833828533909144072330080184872",
           "0.020222743096546488827333191237246591760639730704704148795009602627175281013458865557238240830203973690492882935553111519759474835556686812553430134442713057622274348510526931333124437928482001301214776111358848026164688447150395879262705323533149853785508106018564787140458587165768350143018847840661371060583295055600185059031740386698369796694993301139380463919242175430688496936048982929502368583755",
           "0.022134359839997490880406060727014362125793630864951833270601574861700025448109186301755833862830343973100181180995751924514611578149687732808840212399134735559355116816700346196685057916461444889179117142807858883574426062415931319905031345377305247124198345242221848293630112578508459623098934259229893301963120289071135120089672338617200639338087265986570094025605269870532431816151660553037324591448",
           "0.024045895662035785157706623778569329505432699504171659064296747761761946910295074291838826981712792368282349553905175996858645967280135417577488810651594776626562252686758463813820791031580579721782251705600570838044158357396828391251385978824608388341556751951593319015721256942139947235672567093309227673005475237474351604086627391812539914665025071874865132198040631867741985556198082915821590691115",
           "0.025957343574242448364285479040344211549214421772336408904791690210449825580868841229728937909141136116652894475476991471494524501035809956288247010861464302066154254736425460468024350571584780489213056428098560686376999562953811729945811217829999284678155635497607933780499844559793793320393174691097636750027421158473345884329284455326733063073401001936304522649050748641723222539485352644237494855455",
           "0.027868696588519948373400137317728007608587427701668964330844960393232962659347088604492706251530487148805683505883152882204177723404307465324383408418716579280515871949790204586171885981761699066301009410176801635003961167315448162116236938093263170474354430610732127054993766008385425707409224417795873645410443727472246508894027549288331903202127478347028519320691939639175445464091747341289465807076",
           "0.029779947717117692140641616959423088975059158964671307393654571763491553590582559015283533532986018614029995854278748039155177088839414450454409571267110720713374288363670804692289394770804051182510495900322915294613479146430103948686457060129295537542317247965142780968717375277053102667416124807987527429828758851341378395347636356414345553353015296944031211364374602764176767114282660874356057979248",
           "0.03169108997265757234968007548831539092664261419258315028339932724609641378752993073240305448323210275610250474048387518003293691429670190316839718892647475151605557113852966925288722557261748981001717642661075734993748510506277165730472207647763678204813714194082852502621623007244214605646094787730992510318924772328958129857640953691466395972638014259980360897599169526022683004620965496473543834477",
           "0.033602116368159512696233026049065446239504816931754722314316906991719894135599777826750609185018647633748023602662568553364455253741994701511752478506175671449029859193583332380924298164350627023797218098539441228986492601142199774579432202424360114528105854714450548265654769162069979862894518829365029262734944196495563243510942310738160350648115273816371164862001848102516957052031698646996790510953",
           "0.035513019917067011716864665791693591701266221069066142988726516832036728613300457006403299601385922973285573124147669717618486570986860008564309641657943172757912451506572783449830586163814477502720341010870119231030511664725149033496023356743601581035789746158436049009960920433265660001864258067191594425969638702851317416487965304378219676062220223886417078628342731900948906319081697480745936074075",
           "0.037423793633272685069230163289099410548201460485083250584815890545695501760634737348027106921155349564805321222282824709072582234185849564440670000847571561359324629877368620222775856068569278590582843550096096627510109210574429458414697641703261368397349100700647508781941466979105505468662952228018391181441110331804231754679360190526351574820397399161022517352576803713854137820941977691824804918229",
           "0.039334430531143806170384413477273936914540782490679532559795199052084263353981243128408259086619593938946158294175465792633584421592350011455198124846778194417281005620258982615227234871780679327289232072858751285307955384102986030326321993554199425677956531006546270705693578070953933490084008156062047268010604414954653769711129551190023978078265436680151306319349753427763894284339669614362603225399",
           "0.041244923625547845099780771389235760645292527949481044094490883777921290140650504322516472637184803214130714431279474404434513167088543202171922178874118967991301744186858054508473413647148744260057423931033177973119110921967581379650616945061418716237102199971097086754171168500651650526166023618723542873290120298640370217463634503988257867984189842393302366909916756244585593509804376197012137137735",
           "0.043155265931878005673591620105503250213648422568500347993529171942176855000247082147052189275394127432389966427668413127665366489709229602926384588361071126297040547180622989395253518259197742053810419372452372506534313264090033740095357809571640645325965973975968081307185286025492280541542290934505383760407808393490207181258819590768103829347539943429968533891105456889889207923129412409325200337396",
           "0.045065450466078760596989313842010956972889269629840996618541471076456145341576346037140737554087089598232657340541865108389258265511462307708855932308284610805209084413460614598570062354088931179587984702006847566162492414964136182928014974554852612762940578089297583097434972291633301329615187665009664697057933900728259220674217919826618275818024987757805509580781060051826778978395144370327934582031",
           "0.046975470244671384601033063916347240271496301008030038885300525267985292259051299166038486168462131096926077920611254966322965395673221324506213593415263189667393727124978454125104192236004303056348320706091018781990803164595039969574510002737990997634364247271320535459089400357647302806652831431190979518269432197656547265417669309719787317687585776029758282182901563328846367691507505917911626458378",
           "0.048885318284779485470814703449342779874157141188272539012461507790598796169844054420701400747288103863351008593834375336008952419161297787680355981672505884405645257094616683497040471721980241679616912483788844007649447969703512155758482444019770785766752697959554691538291379700303132353876869628124563105576278585509058450081192711918856522653293896204836307264696289474219755720645459588331890558889",
           "0.050794987604154532871523976044438688478988546522275702591135165295075684834775069782087540212155311599924632216878717363279038406203747968637573994688146329296755023729170505930935028107359530042909822369245609091696704070694133386183688825674952803613566914142157159664703170379754943610785780639205752343103591085978554489192416588288410803225066825839880971435465232206429722600557780654218330146221",
           "0.052704471221201384879102044313723519214541830844294339002006150295000261803629777930668202713875629368914012613840180174156598399096984605842000917709330893871365961242812860633022132663435078869433262371062200555333508565564704972551921154864927879748108180002977668648111714062992755985320189721615088748697314805428967770533095416911693326757965362169171309026356339280576158225848771071227224373815",
           "0.054613762155003812122160305957618147686113745063989650607179163157398820817715079449268802189362998719256947035541552524839497119721884536865240705017403409053301625152685151644761334409897199810093505097528650897729279062117617766845257388412635800174656504897022571125534403010151970450307624961920347280823962725005822339087173498538012712497715370388872960900453285934131578336748483622553313648565",
           "0.056522853425350019441850338124226420404372628229474954730316461224029279793449121148941830155705856364290655296696008865349058650396297667951745639266344993911111272076019522476315254920931469406381088740392436352997178777214659473302034620137440843681809428909274912969436139312159105442160243076856936138443854294531618433726198293972487505166088883354213979143973990964298454127970039847456893860203",
           "0.058431738052758164976379864942430396555977098734986011309321413104802080798824039565461521608355122102022725461220269967251619843394860641070621951433765763369761658916816880868188275032453137184211673934079277285593935159478757148621410070196500221526968060556586710920750728824112282461003224923995158937905479480579208549847478185757177039153501786271361921802927586114942206193581554801605680183774",
           "0.060340409058501876576879058406717550375449897922866349024612531373677724965692896763451690034750214807393086060232183962741893455630800282528706094634312497867863194252914455062127544671909424505367559503649890477570116767021509485710626043408265846213906257952113003324887350087083283630715857354116849013376841533857321740197047217435805280085872071354373524395263864933170034771001607559366437835689",
           "0.06224885946463576546133123915706193746686049729742751789281333411875145041642253752249398319274836506892916659694331849766284472811963607782488993959669913942440995816339529777656740297867471191540909520680122909159182411803441391065468297111274482089347737322313334144161837035757661055283810143238846811496286086926075008068489724098160672664550444107807861952850135669761489736449089327971794824585",
           "0.064157082294020937013292141108273585068933185681722806586173136512969086561151000586083228827704474922133659368208499428517560937267711287558275856325851735527047525549135602809953924346232393575354505341516408877034795144989051337120442048812961016500803766780090428411925756071299228254760633032932651170483895163866111455935100454818941073761804284777739005007991141161823471943803047351369535073521",
           "0.066065070570350498632132342303164932774411476452581548988386145454309844170784631219900687545375669726062411278210148833761887075889382631711319762066717518842787273543143969874177059386003262516941215168234997245420735913778365997498382320917159517052861653706668051150014889560865161606396037665048820655743861710636504450723955435275019348223982732095398949832565423361714391500479155224225613768011",
           "0.067972817318175064541548243754498015429928588516517183822313264446879635682093113384264993280408482780193047687370305892309106748875519486540879020142902362307237689684697063792225838111742241714789823601251998299331254097504567214769822096812914869886418586879378868131071702869283011709161582762527864014863950459121468893761825841337802563429112718615110803469902519051203360473993644451463303576859",
           "0.069880315562928257463098098362562671709183706398122146969098129179472379455539984270317889382725678551314678030902515782188203802362460214070192378666311369730755912670660970378408284876465192356340335277225457776091515934608751710356445501169982098020701963512215973413220903547301855847279762573955110834619967590588317288171580291576756417265600792610005176888567344924086299458709493921684370833814",
           "0.071787558330952207061531053207739391124226218288071373402959685303573894071937999096931283415397385296306228484128726125223568292760110763075739612174182840148193517540379302896938375357744754831885570482299802985278525692833941172622048860945698352794727105726197891701070706523197225463566498764869186174536278875440544967870368440256565084805804998877012238900798874720340885805165811704445925369809",
           "0.07369453864952304506868897057861922170414313661538863465614770246556638808351254050794164760581857499175318127117745597595046399453737415234966090460819383278338184265518294393589316067434296307320873514873375534499000895507092920123162793722742278902956263178675873980121089525814840115300730124457959632013360479776554262210801798434607194598511786777350540581826836696200671400628870250659962729555",
           "0.075601249546876396992772935963040077834759122887264324876826746597298507042570795315940378957369646666545616927930046677179646722187557860442506025110541968257406052679604681892086517951648112201767029044637142334937786145563391280677987040697732028896674786378870229133792190093646530752257521390201161387698590619925316803401938362974478273195688740273778224688121208296858072701246429755713258468481",
           "0.077507684052232870319778844857360483087988665809157375994805235390695227945273826842335916971262245322463861698987988491433092759415620685754813456247989287157909601376742141647886684380473379403067343977633347655683745871434202230624968375294472571053679501320680048739346844244553807193670436672857163839591129928936603341033670518982144557944158002407590669543043048232580344768659176122645763500513",
           "0.079413835195823539113919284592789131508074280056913456617148863190077260584186476150168101766032692499585019572300009477107342325731806003539645749720998260074894005503243715777788187754550286488316966320623707403846973934258796072434720381188302956748572794547906591019552569722211941807674118070825610462581312695693650893380241875873254675082431417454526920871842555422162216221640850141209989864308",
           "0.081319696008915424923862092389736899811819511475112310887916890436246797704730704673729056088324784096921353721791055102563812592375189346816348952621603248827528923433998597558478366712658235656924601774921095079356565867874041683517103417362062033545480747089630436317004907748679995334911923789802669273406449877710501792905734235548100216242648355048174292232350538531258791292074224222773259366265",
           "0.083225259523836973901629476483826882570495097409722948211156651871411915926347698443317606324912095013846386144848076019334983060323996364575622231284156371311209803383868297666027142776492604713451401116304951916037351315288808149328656282592946622481248077426878390562084748274992526750980236994304445617883874613002677387890383751048690275480465558930319669630406765794141303531112767626885032234711",
           "0.085130518774003530041015433371012162123590338565297712899451572274044870858132401685294989726432756808251618198809497117031428754224182463768189333992610411750324249283786647605964593556580841488686189374310703328490638378071341189999989736919850819640255203388192531225871093915262442913255553964348789674386732423763999398384843950222967487918049677831350991752153610543052586794779289758903475438463",
           "0.087035466793942804442393380943589773874807384137701777807755858606555806090045313793349743667697666495784828540247587627381581545620918795107877715086227932192756144780086856515641298195558204484728115519445514732755572178929839231921546119143469647706999890259163924570037168105911942268212237224359201159897493303064534985539474450028748960081579369980283792804593122976296597796047722827878891322393",
           "0.088940096619320340510800454481897179643787714504988904779549430422905882044782054375828906100456879992313640144934242418129400679706865644316664458061953684856607341811667576418163339843372041295322709797007871604740491453013916791024918954258557372755707588171502529270934617054742841996068375532958173787119651672031238405892943090742861150287144368470297356054911834742041692715934237148148936307154",
           "0.090844401286964974994199780075024285173534240616972938308566124002318176156233343333797652771250228033597767340846476094210813299723066344955542455246897302945908336756464582189829335315055318509171623072382573656172991307091055717376996812421994002305994837615827430425975713541786272473174908117299253875064746541808768776888139494200307162476704515967971265236163183394620211890252072718610942571965",
           "0.092748373834894294768837248013614869545920040757666771889792264347367335922283987944081370356939214799510822558638935486112190359701790662792925773186897671244831971450474952036618937833706899006048469791213129922302798462311426294239282200248625552430083292818594731518350634587677158329361662244008606299358410273467156168469937529868888478684257396004386633746334360279683178784117372066095688934486",
           "0.094652007302340089278624856973167138217258137565762699413364163993950641989798336903224059425798872308451866930780842878435503832366316245159154044350517727312654980455290163514021860802028251981954724580292247887851185639950862672925642859475325430465650514258094310141033116303195908372018392349011340787373492402788283020594141795669947097771918542864972570255253465754192708165241608041715996375567",
           "0.09655529472977379853549858833033074225823562054271495313739665642376685099661084023094270272485976247900824483810911634635819558334630910267353320029261330296977292720266655308513559530586843550229208517388789783011887450865488554143475302590353915732321663418057567573042594801866258948380684000769091353165879953111046260532796891917772727185993569684246844052518121013717183610828519193371796413317",
       }};

   T half_pi = static_cast<T>("1.57079632679489661923132169163975144209858469968755291048747229615390820314310449931401741267105853399107404325664115332354692230477529111586267970406424055872514205135096926055277982231147447746519098221440548783296672306423782411689339158263560095457282428346173017430522716332410669680363012457064");

   max_err = 0;
   for (unsigned k = 0; k < near_one.size(); k++)
   {
      static const T euler_gamma = static_cast<T>("5.77215664901532860606512090082402431042159335939923598805767234884867726777664670936947063291746749514631447249807082480960504014486542836224173997644923536253500333742937337737673942792595258247094916008735203948165670853233151776611528621199501507984793745085705740029921354786146694029604325421519e-1");
      T              val         = cos(half_pi - (euler_gamma + k) / 523);
      T              e           = relative_error(val, T(near_one[k]));
      unsigned       err         = e.template convert_to<unsigned>();
      if (err > max_err)
         max_err = err;
      val = cos(-half_pi + (euler_gamma + k) / 523);
      e   = relative_error(val, T(near_one[k]));
      err = e.template convert_to<unsigned>();
      if (err > max_err)
         max_err = err;
   }
   std::cout << "Max error was: " << max_err << std::endl;
#if defined(BOOST_INTEL) && defined(TEST_FLOAT128)
   BOOST_TEST(max_err < 8000);
#else
   BOOST_TEST(max_err < 750);
#endif

   //
   // Test with some exact binary values as input - this tests our code
   // rather than the test data:
   //
   static const boost::array<boost::array<T, 2>, 8> exact_data =
       {{{{0.5, static_cast<T>("0.877582561890372716116281582603829651991645197109744052997610868315950763274213947405794184084682258355478400593109053993")}},
         {{0.25, static_cast<T>("0.968912421710644784144595449494189199804134190287442831148128124288942561184523327264655202799685025510352709626116202617")}},
         {{0.75, static_cast<T>("0.731688868873820886311838753000084543840541276050772482507683220220750082501569499540967562610201174960122884908227300721")}},
         {{std::ldexp(1.0, -20), static_cast<T>("0.99999999999954525264911357034690133684385823577463126432241468890539365027135494672267164697779879113636143901797362388")}},
         {{2, static_cast<T>("-0.416146836547142386997568229500762189766000771075544890755149973781964936124079169074531777860169140367366791365215728559")}},
         {{5, static_cast<T>("0.283662185463226264466639171513557308334422592252215944930359066586151456767382702286176981668344573238827368717546699737")}},
         {{10, static_cast<T>("-0.839071529076452452258863947824064834519930165133168546835953731048792586866270768400933712760422138927451054405350243624")}},
         {{8.5, static_cast<T>("-0.60201190268482361534842652295699870029606776360435523539636606145572515876770619546025351418378467287262574566665150299")}}}};
   max_err = 0;
   for (unsigned k = 0; k < exact_data.size(); k++)
   {
      T        val = cos(exact_data[k][0]);
      T        e   = relative_error(val, exact_data[k][1]);
      unsigned err = e.template convert_to<unsigned>();
      if (err > max_err)
         max_err = err;
      val = cos(-exact_data[k][0]);
      e   = relative_error(val, exact_data[k][1]);
      err = e.template convert_to<unsigned>();
      if (err > max_err)
         max_err = err;
   }
   std::cout << "Max error was: " << max_err << std::endl;
   BOOST_TEST(max_err < 20);

   BOOST_TEST(cos(T(0)) == 1);
#if defined(BOOST_INTEL) && defined(TEST_FLOAT128)
   BOOST_TEST(fabs(cos(half_pi)) < 4 * std::numeric_limits<T>::epsilon());
#else
   BOOST_TEST(fabs(cos(half_pi)) < std::numeric_limits<T>::epsilon());
#endif

#include "sincos.ipp"
   max_err = 0;
   for (unsigned k = 0; k < sincos.size(); k++)
   {
      T        val = cos(sincos[k][0]);
      T        e   = relative_error(val, sincos[k][2]);
      unsigned err = e.template convert_to<unsigned>();
      if (err > max_err)
         max_err = err;
   }
   std::cout << "Max error was: " << max_err << std::endl;
   BOOST_TEST(max_err < 20);

   if (has_poor_large_value_support<T>::value)
   {
      T bug_value = 12 / std::numeric_limits<T>::epsilon();
      for (unsigned i = 0; i < 20; ++i, bug_value *= 1.1)
      {
         BOOST_TEST(cos(bug_value) == 1);
      }
   }
}

int main()
{
#ifdef TEST_BACKEND
   test<boost::multiprecision::number<boost::multiprecision::concepts::number_backend_float_architype> >();
#endif
#ifdef TEST_MPF_50
   test<boost::multiprecision::mpf_float_50>();
   test<boost::multiprecision::mpf_float_100>();
#endif
#ifdef TEST_MPFR_50
   test<boost::multiprecision::mpfr_float_50>();
   test<boost::multiprecision::mpfr_float_100>();
#endif
#ifdef TEST_MPFI_50
   test<boost::multiprecision::mpfi_float_50>();
   test<boost::multiprecision::mpfi_float_100>();
#endif
#ifdef TEST_CPP_DEC_FLOAT
   test<boost::multiprecision::cpp_dec_float_50>();
   test<boost::multiprecision::cpp_dec_float_100>();
#ifndef SLOW_COMPLER
   // Some "peculiar" digit counts which stress our code:
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<65> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<64> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<63> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<62> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<61, long long> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<60, long long> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<59, long long, std::allocator<char> > > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<58, long long, std::allocator<char> > > >();
#endif
#endif
#ifdef TEST_FLOAT128
   test<boost::multiprecision::float128>();
#endif
#ifdef TEST_CPP_BIN_FLOAT
   test<boost::multiprecision::cpp_bin_float_50>();
   test<boost::multiprecision::number<boost::multiprecision::cpp_bin_float<35, boost::multiprecision::digit_base_10, std::allocator<char>, boost::long_long_type> > >();
#endif
   return boost::report_errors();
}
